library(tidyverse)
library(plotly)
# 1. SINCRONIZACIÓN DE NIVELES (Vital para evitar el error de colores)
niveles_oficiales <- c(
"< 01m", "01-11m", "01-05a", "06-11a",
"12-17a", "18-29a", "30-59a", "60a >"
)
# 2. DEFINICIÓN DE COLORES
colores_etapa <- setNames(
c("#9e0142", "#d53e4f", "#f46d43", "#fdae61",
"#fee08b", "#e6f598", "#66c2a5", "#3288bd"),
niveles_oficiales
)
# 3. DATOS
raw_data <- tribble(
~Etnia, ~Etapa, ~`2019`, ~`2020`, ~`2021`, ~`2022`, ~`2023`, ~`2024`, ~`2025`,
"Afroperuana", "01-11m", 0, 0, 0, 1, 0, 0, 0,
"Afroperuana", "06-11a", 1, 0, 0, 0, 0, 0, 0,
"Afroperuana", "12-17a", 1, 0, 0, 1, 1, 0, 0,
"Afroperuana", "18-29a", 9, 21, 31, 2, 0, 0, 0,
"Afroperuana", "30-59a", 5, 5, 1, 9, 3, 4, 3,
"Afroperuana", "60a >", 2, 0, 0, 1, 1, 1, 1,
"Amazónica", "< 01m", 1, 0, 1, 0, 0, 0, 0,
"Amazónica", "01-11m", 1, 1, 6, 4, 2, 3, 0,
"Amazónica", "01-05a", 11, 9, 10, 20, 19, 24, 9,
"Amazónica", "06-11a", 18, 17, 15, 15, 27, 23, 13,
"Amazónica", "12-17a", 35, 41, 43, 72, 144, 73, 48,
"Amazónica", "18-29a", 127, 100, 109, 175, 431, 270, 165,
"Amazónica", "30-59a", 256, 211, 230, 456, 737, 552, 249,
"Amazónica", "60a >", 96, 60, 94, 165, 574, 243, 118,
"Andina", "01-11m", 0, 1, 0, 2, 0, 5, 0,
"Andina", "01-05a", 5, 0, 1, 7, 6, 13, 2,
"Andina", "06-11a", 7, 5, 15, 1, 4, 3, 1,
"Andina", "12-17a", 7, 5, 13, 12, 18, 14, 14,
"Andina", "18-29a", 51, 59, 66, 78, 96, 88, 68,
"Andina", "30-59a", 74, 56, 85, 158, 159, 174, 125,
"Andina", "60a >", 83, 62, 42, 97, 157, 128, 108
)
plot_data <- raw_data %>%
pivot_longer(cols = `2019`:`2025`, names_to = "Anio_Texto", values_to = "Casos") %>%
mutate(
# Mantenemos Año_Num para el eje X, pero no lo mostraremos en el tooltip
Año_Num = as.numeric(Anio_Texto),
Etapa = trimws(Etapa),
Etapa = factor(Etapa, levels = niveles_oficiales),
Etnia = case_when(
Etnia == "Afroperuana" ~ "Población afroperuana",
Etnia == "Amazónica" ~ "Poblaciones amazónicas",
Etnia == "Andina" ~ "Poblaciones andinas",
TRUE ~ Etnia
)
) %>%
filter(!is.na(Etapa))
# 4. GRÁFICO
g_curso_vida <- ggplot(plot_data, aes(x = Año_Num, y = Casos, fill = Etapa)) +
geom_area(
position = "stack",
alpha = 0.85,
color = "white",
size = 0.1,
# AQUI ESTÁ LA SOLUCIÓN AL ERROR DE NIVELES:
# Agregamos group = Etapa explícitamente para que ggplot no se confunda con el 'text'
aes(
group = Etapa,
# Definimos exactamente qué queremos ver. NO incluimos Año_Num.
text = paste0("<b>", Etapa, "</b><br>",
"Casos: ", Casos, "<br>") # Usamos el texto del año si quieres que aparezca el año limpio
)
) +
facet_wrap(~Etnia, ncol = 1, scales = "free_y") +
scale_fill_manual(values = colores_etapa, name = "Curso de Vida") +
scale_x_continuous(breaks = 2019:2025) +
labs(
title = "Tuberculosis en afroperuanos, andinos y amazónicos por curso de vida",
subtitle = "2019-2025",
x = "",
y = "Número de Casos"
) +
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5, face = "bold", size = 14),
strip.text = element_text(face = "bold", size = 12),
strip.background = element_rect(fill = "#f0f0f0", color = NA),
legend.position = "bottom",
panel.grid.minor = element_blank(),
axis.title.y = element_text(margin = margin(r = 15), face = "bold")
)
# 5. INTERACTIVIDAD
# tooltip = "text" es la clave: le dice a Plotly que SOLO muestre lo que pusimos en aes(text=...)
# e ignore las variables automáticas como Año_Num.
ggplotly(g_curso_vida, tooltip = "text") %>%
layout(
hovermode = "x unified",
legend = list(
orientation = "h",
xanchor = "center",
x = 0.5,
y = -0.05,
title = list(text = "Curso de vida")
),
margin = list(t = 80, b = 80, l = 80)
)